using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;

using NerdBank.Tools;

namespace Lds.Ward.PhotoDirectory.Logic
{
	public class FamiliesToPdf
	{
		protected FamiliesToPdf(IEnumerable<Family> families, string unitName, string xsltFileName)
		{
			this.families = families;
			this.unitName = unitName;
			this.xsltFileName = xsltFileName;
		}

		string xsltFileName;
		protected string XsltFileName { get { return xsltFileName; } }
		private string unitName;
		public string UnitName
		{
			get { return unitName; }
			set { unitName = value; }
		}
		private SizeF photoSize = new SizeF(48, -1);
		public SizeF PhotoSize
		{
			get { return photoSize; }
			set { photoSize = value; }
		}
		IEnumerable<Family> families;
		protected IEnumerable<Family> Families { get { return families; } }

		protected virtual XmlReader GetXml(Uri photosDirectory, TempFileCollection thumbnailFiles, MediaQuality quality)
		{
			StringBuilder xml = new StringBuilder();
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.CloseOutput = true;
			settings.Encoding = Encoding.UTF8;
			using (XmlWriter writer = XmlTextWriter.Create(xml, settings))
			{
				writer.WriteStartElement("families");
				foreach (Family f in families)
					f.ToXml(writer, photosDirectory, thumbnailFiles, quality);
				writer.WriteEndElement();
			}
			return new XmlTextReader(new StringReader(xml.ToString()));
		}
		protected virtual XmlReader GetFilteredXml(XmlReader familiesXml, PostalAddress address, string areaCode)
		{
			XmlDocument doc = new XmlDocument();
			doc.Load(familiesXml);
			XPathNavigator nav = doc.CreateNavigator();
			if (areaCode != null && areaCode.Length > 0)
			{
				if (!areaCode.EndsWith("-")) areaCode += "-";
				XPathNodeIterator phones = nav.Select("/families/family//@phone[starts-with(., '" + areaCode + "')]");
				foreach (XPathNavigator phone in phones)
					phone.SetValue(phone.Value.Substring(areaCode.Length));
			}

			if (address != null)
			{
				XPathNodeIterator cities = nav.Select("/families/family[@city='" + address.City + "']/@city");
				XPathNodeIterator states = nav.Select("/families/family[@state='" + address.State + "']/@state");
				XPathNodeIterator zips = nav.Select("/families/family/@zip[starts-with(.,'" + address.PostalCode + "')]");

				foreach (XPathNavigator city in cities)
					city.DeleteSelf();
				foreach (XPathNavigator state in states)
					state.DeleteSelf();
				foreach (XPathNavigator zip in zips)
					zip.DeleteSelf();
			}

			return new XmlTextReader(new StringReader(nav.OuterXml));
		}

		protected virtual XslCompiledTransform Xslt
		{
			get
			{
				XslCompiledTransform xslt = new XslCompiledTransform(true);
				using (XmlReader xsltreader = new XmlTextReader(EmbeddedResources.GetFileStreamFromAssembly(xsltFileName, Project.DefaultNamespace)))
					xslt.Load(xsltreader);
				return xslt;
			}
		}

		protected virtual XsltArgumentList GetXsltParameters()
		{
			XsltArgumentList args = new XsltArgumentList();
			args.AddParam("unit", string.Empty, UnitName);
			args.AddParam("date", string.Empty, DateTime.Now.ToString("MMM d \"'\"yy"));
			args.AddParam("photoWidth", string.Empty, PhotoSize.Width.ToString() + "mm");
			return args;
		}

		protected virtual XmlTextReader GetFO(XmlReader familiesXml)
		{
			StringBuilder fo = new StringBuilder();
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			XmlWriter xw = XmlTextWriter.Create(new StringWriter(fo), settings);
			Xslt.Transform(familiesXml, GetXsltParameters(), xw);
			saveRawFO(fo.ToString());
			return new XmlTextReader(new StringReader(fo.ToString()));
		}

		[Conditional("DEBUG")]
		private static void saveRawFO(string p)
		{
			string filename = Environment.ExpandEnvironmentVariables(@"%TEMP%\fo.xml");
			using (StreamWriter sw = new StreamWriter(filename))
				sw.Write(p);
		}

		protected virtual void FOtoPdf(XmlTextReader familiesFO, Stream pdf,
			org.apache.fop.apps.Driver.UpdateStatusCallback update)
		{
			using (TempFileCollection tfc = new TempFileCollection())
			{
				tfc.KeepFiles = false;
				string foFilename = tfc.AddExtension("xml");
				string pdfFilename = tfc.AddExtension("pdf");
				familiesFO.Read();
				using (StreamWriter sw = new StreamWriter(foFilename))
					while (!familiesFO.EOF)
						sw.Write(familiesFO.ReadOuterXml());

				java.io.FileInputStream source = new java.io.FileInputStream(foFilename);
				org.xml.sax.InputSource input = new org.xml.sax.InputSource(source);
				java.io.FileOutputStream output = new java.io.FileOutputStream(pdfFilename);
				org.apache.fop.apps.Driver driver = new org.apache.fop.apps.Driver(input, output);
				driver.setRenderer(org.apache.fop.apps.Driver.RENDER_PDF);
				driver.setUpdateStatusCallback(update);
				driver.run();
				output.close();

				using (FileStream fs = new FileStream(pdfFilename, FileMode.Open))
				{
					byte[] buffer = new byte[4 * 1024];
					while (fs.Position < fs.Length)
					{
						int count = fs.Read(buffer, 0, buffer.Length);
						pdf.Write(buffer, 0, count);
					}
				}
			}
		}


	}
}
